Се­год­ня пол­нодис­ковое шиф­рование — это нор­ма. В Windows для это­го есть BitLocker, в macOS — FileVault и APFS. В Linux для тех же целей слу­жит dm-crypt и LUKS. В этой статье я покажу, как уста­новить ОС на шиф­рован­ный кор­невой раз­дел, нас­тро­ить авто­раз­бло­киров­ку при заг­рузке с помощью модуля TPM и новой фичи systemd-cryptsetup, а так­же резер­вировать ключ вос­ста­нов­ления в Active Directory. Все трю­ки будем делать в Arch Linux, хотя ты с лег­костью смо­жешь пов­торить их в сво­ем любимом дис­три­бути­ве.

Шиф­рование дис­ков в Linux реали­зует­ся с помощью под­систе­мы dm-crypt Crypto API (встро­ено в ядро начиная с вер­сии 2.6). Под­систе­ма dm-crypt работа­ет бла­года­ря модулю ядра, который отоб­ража­ет шиф­рован­ный диск в вир­туаль­ное устрой­ство. На вид оно ничем не отли­чает­ся от обыч­ного блоч­ного устрой­ства хра­нения дан­ных. Для управле­ния клю­чами шиф­рования исполь­зует­ся раз­дел LUKS (Linux Unified Key Setup). Фор­мат LUKS поз­воля­ет исполь­зовать до вось­ми клю­чей шиф­рования для одно­го раз­дела.

 

Как устроен раздел LUKS

Раз­дел LUKS име­ет сле­дующий фор­мат.

Он начина­ется с заголов­ка phdr, далее за ним сле­дуют сло­ты с клю­чевы­ми дан­ными (KM1, KM2, ..., KM8). За клю­чевы­ми дан­ными рас­полага­ются дан­ные, шиф­рован­ные мас­тер‑клю­чом.

За­голо­вок phdr хра­нит информа­цию о про­токо­ле и режиме шиф­рования, дли­ну клю­чей, иден­тифика­тор UUID и кон­троль­ную сум­му мас­тер‑клю­ча.

В LUKS для одно­го зашиф­рован­ного раз­дела зарезер­вирова­но восемь сло­тов, в каж­дом из которых может хра­нить­ся отдель­ный ключ. Любой из вось­ми клю­чей может быть исполь­зован для рас­шифров­ки раз­дела.

За­голо­вок и сло­ты клю­чей мож­но хра­нить на дру­гом физичес­ком носите­ле, отдель­но от зашиф­рован­ных дан­ных, тем самым реали­зуя мно­гофак­торную защиту. Но при утра­те заголов­ка или сло­тов получить дос­туп к зашиф­рован­ным дан­ным ста­новит­ся невоз­можно.

Для управле­ния шиф­ровани­ем дис­ков исполь­зует­ся ути­лита cryptsetup.

С помощью этой ути­литы воз­можно:

  • соз­давать шиф­рован­ные раз­делы LUKS;
  • от­кры­вать и зак­рывать раз­делы LUKS;
  • уп­равлять сло­тами клю­чей;
  • дам­пить заголо­вок LUKS и мас­тер‑ключ.

Для работы cryptsetup тре­буют­ся пра­ва супер­поль­зовате­ля и пароль шиф­рования.

 

Шифруем

Пе­рехо­дим к прак­тике! Шиф­ровать мы будем толь­ко рутовый раз­дел. Есть, конеч­но, экзо­тичес­кие кей­сы, ког­да, помимо раз­дела root, шиф­рует­ся еще и раз­дел boot, но такая кон­фигура­ция под­держи­вает­ся не все­ми заг­рузчи­ками и не счи­тает­ся стан­дар­тной и рекомен­дован­ной. Мы же вооб­ще не будем исполь­зовать заг­рузчик, так что оставля­ем boot в покое и шиф­руем все осталь­ное.

Нач­нем собирать нашу сис­тему, заг­рузив­шись с уста­новоч­ного дис­ка.

Для начала раз­метим диск сле­дующим обра­зом: sda1 для /boot, sda2 для /. Схе­му раз­делов выбира­ем GPT.

$ parted /dev/sda mklabel gpt mkpart primary fat32 1MiB 501MiB
$ parted /dev/sda set 1 esp on
$ parted /dev/sda mkpart primary btrfs 501MiB 100%

Да­лее соз­дадим наш LUKS на sda2.

$ cryptsetup luksFormat /dev/sda2

WARNING!
========
This will overwrite data on /dev/sda2 irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda2:
Verify passphrase:
cryptsetup luksFormat /dev/sda2 18.16s user 1.88s system 80% cpu 24.742 total

Взгля­нем теперь на LUKS.

$ cryptsetup luksDump /dev/sda2
LUKS header information
Version: 2
Epoch: 3
Metadata area: 16384 [bytes]
Keyslots area: 16744448 [bytes]
UUID: e04b5b87-6bfc-4f73-83b0-36f91d52f141
Label: (no label)
Subsystem: (no subsystem)
Flags: (no flags)

Data segments:
0: crypt
offset: 16777216 [bytes]
length: (whole device)
cipher: aes-xts-plain64
sector: 512 [bytes]

Keyslots:
0: luks2
Key: 512 bits
Priority: normal
Cipher: aes-xts-plain64
Cipher key: 512 bits
PBKDF: argon2i
Time cost: 7
Memory: 483194
Threads: 2
Salt: a8 d7 82 ce 89 c8 0f d6 29 18 83 e5 5d 9d a7 f1
a2 6d 66 81 70 db c4 82 cc fb ae 81 4c 7f ed 0c
AF stripes: 4000
AF hash: sha256
Area offset:32768 [bytes]
Area length:258048 [bytes]
Digest ID: 0
Tokens:
Digests:
0: pbkdf2
Hash: sha256
Iterations: 149967
Salt: 87 ac 6f 61 75 fb 91 14 63 5d ca 5d 1c 25 ef 42
7b af 51 63 34 eb 26 d5 d7 be 7a 78 7b 2a 25 f1
Digest: da e7 fd 26 59 85 5d 5e 34 79 2a fa 20 95 f1 83
13 10 0d 0e a3 58 a6 0e 33 b0 f0 73 e8 0a a1 1e

Вид­но, что мы задей­ство­вали один из вось­ми дос­тупных сло­тов с клю­чами — дос­туп к нему огра­ничен паролем.

Ал­горитм шиф­рования по умол­чанию (aes-xts-plain64) нас устро­ит. Перечень всех под­держи­ваемых алго­рит­мов зависит от ядра (заг­ляни в /proc/crypto). Мож­но про­тес­тировать работу с ними.

$ cryptsetup benchmark
# Tests are approximate using memory only (no storage IO)
PBKDF2-sha1 1817289 iterations per second for 256-bit key
PBKDF2-sha256 2302032 iterations per second for 256-bit key
PBKDF2-sha512 1646116 iterations per second for 256-bit key
PBKDF2-ripemd160 903944 iterations per second for 256-bit key
PBKDF2-whirlpool 681778 iterations per second for 256-bit key
argon2i N/A
argon2id N/A
# Algorithm | Key | Encryption | Decryption
aes-cbc 128b 1126.9 MiB/s 2966.7 MiB/s
serpent-cbc 128b 98.6 MiB/s 736.0 MiB/s
twofish-cbc 128b 226.0 MiB/s 404.9 MiB/s
aes-cbc 256b 868.5 MiB/s 2594.2 MiB/s
serpent-cbc 256b 107.9 MiB/s 779.9 MiB/s
twofish-cbc 256b 241.6 MiB/s 433.4 MiB/s
aes-xts 256b 3152.7 MiB/s 3146.5 MiB/s
serpent-xts 256b 676.5 MiB/s 665.9 MiB/s
twofish-xts 256b 400.6 MiB/s 410.4 MiB/s
aes-xts 512b 2614.9 MiB/s 2600.2 MiB/s
serpent-xts 512b 687.1 MiB/s 711.3 MiB/s
twofish-xts 512b 413.0 MiB/s 417.2 MiB/s
cryptsetup benchmark 9.54s user 23.98s system 107% cpu 31.226 total

От­кро­ем раз­дел.

$ cryptsetup open /dev/sda2 cryptroot
Enter passphrase for /dev/sda2:

Пос­ле этих манипу­ляций у нас в сис­теме стал дос­тупен новый раз­дел cryptroot.

$ lsblk /dev/sda
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 8G 0 disk
├─sda1 8:1 0 500M 0 part
└─sda2 8:2 0 7.5G 0 part
└─cryptroot 253:0 0 7.5G 0 crypt

Соз­дадим на раз­делах фай­ловые сис­темы.

$ mkfs.fat -F32 /dev/sda1
$ mkfs.btrfs -f -L "archroot" /dev/mapper/cryptroot

Для про­дол­жения уста­нов­ки при­мон­тиру­ем наши раз­делы.

$ mount /dev/mapper/cryptroot /mnt
$ btrfs subvolume create /mnt/@root
$ btrfs subvolume create /mnt/@home
$ umount /mnt
$ mount /dev/mapper/cryptroot /mnt -o subvol=@root,ssd,noatime,space_cache,compress=zstd
$ mkdir /mnt/home
$ mount /dev/mapper/cryptroot /mnt/home -o subvol=@home,ssd,noatime,space_cache,compress=zstd
$ mkdir /mnt/boot
$ mount /dev/sda1 /mnt/boot

Заг­лядывая одним гла­зом в ви­ки, уста­нав­лива­ем сис­тему обыч­ным обра­зом.

Не забудем вклю­чить хук encrypt при соз­дании initramfs.

$ vim /etc/mkinitcpio.conf
HOOKS=(base udev autodetect keyboard keymap modconf block encrypt filesystems)
$ mkinitcpio -p linux

Фи­наль­ный этап — нас­трой­ка UEFI для заг­рузки нашего ядра. Напоми­наю, что мы обой­дем­ся без GRUB и будем заг­ружать ядро нап­рямую, исполь­зуя фичу EFISTUB.

До­бав­ляем запись в UEFI.

$ efibootmgr \
--create \
--bootnum=0000 \
--label "Arch Linux" \
--disk /dev/sda \
--part 1 \
--loader /vmlinuz-linux \
--unicode 'cryptdevice=/dev/sda2:cryptroot root=/dev/mapper/cryptroot rootflags=subvol=@root rw initrd=\initramfs-linux.img' \
--verbose

Осо­бое вни­мание обра­ти на парамет­ры ядра! Без них сис­тема прос­то не смо­жет най­ти шиф­рован­ный раз­дел.

Пе­резаг­ружа­емся и убеж­даем­ся, что все работа­ет, как мы хотели: на эта­пе заг­рузки тре­бует­ся ввод пароля для рас­шифро­вания раз­дела.

first_reboot
first_reboot
 

Настраиваем авторазблокировку

Хо­телось бы не вво­дить пароль при заг­рузке каж­дый раз. Для это­го нуж­но нас­тро­ить авто­раз­бло­киров­ку LUKS с помощью чипа TPM.

Убе­дим­ся, что мы счас­тли­вые обла­дате­ли TPM. Необ­ходимая вер­сия TPM — 2.0.

$ test -e /dev/tpm0 && echo PASS || echo FAIL
PASS
$ cat /sys/class/tpm/tpm0/device/description
TPM 2.0 Device
or
$ cat /sys/class/tpm/tpm0/tpm_version_major
2

Ав­тораз­бло­киров­ку сде­лаем с помощью отно­ситель­но новой фичи systemd-cryptsetup.

info

Systemd-cryptsetup добав­лена в systemd начиная с вер­сии 248.

Для начала необ­ходимо про­верить нашу вер­сию systemd.

$ systemctl --version
systemd 249 (249.2-1-arch)
+PAM +AUDIT -SELINUX -APPARMOR -IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY +P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +XKBCOMMON +UTMP -SYSVINIT default-hierarchy=unified

Все отлично. Для работы с TPM нуж­но допол­нитель­но уста­новить пакет tpm2-tss.

$ pacman -Sy tpm2-tss

Да­лее добавим ключ TPM в слот LUKS с помощью тул­зы systemd-cryptenroll.

$ systemd-cryptenroll /dev/sda2 --tpm2-device=auto --tpm2-pcrs=0,4

Не забыва­ем про­верить.

$ systemd-cryptenroll /dev/sda2
SLOT TYPE
---------
0 password
1 tpm2

В коман­де выше мы ука­зали параметр --tpm2-pcrs=0,4. Что такое PCR? Вот оп­ределе­ние из Википе­дии:

PCR — это внут­ренние регис­тры памяти TPM, в которых в зашиф­рован­ном виде содер­жится вся информа­ция о целос­тнос­ти мет­рик сис­темы, начиная с заг­рузки BIOS до завер­шения работы сис­темы. Информа­ция, содер­жаща­яся в PCR, фор­миру­ет корень доверия для изме­рений (RTM). Могут хра­нить­ся как в энер­гонеза­виси­мой, так и в энер­гозави­симой памяти. Эти регис­тры сбра­сыва­ются при стар­те и при перезаг­рузке сис­темы. Спе­цифи­кация пред­писыва­ет минималь­ное количес­тво регис­тров (16), каж­дый регистр содер­жит 160 бит информа­ции. Регис­тры 0–7 зарезер­вирова­ны для нужд TPM. Регис­тры 8–15 дос­тупны для исполь­зования опе­раци­онной сис­темой и при­ложе­ниями. Изме­нения зна­чений PCR необ­ратимы, и их зна­чения нель­зя записать нап­рямую, их мож­но толь­ко рас­ширить новыми зна­чени­ями, которые зависят от пре­дыду­щих. Все изме­нения зна­чений PCR записы­вают­ся в лог изме­нений, который хра­нит­ся в энер­гозави­симой памяти.

PCR Use
PCR0 Core System Firmware executable code (aka Firmware)
PCR1 Core System Firmware data (aka UEFI settings)
PCR2 Extended or pluggable executable code
PCR3 Extended or pluggable firmware data
PCR4 Boot Manager
PCR5 GPT / Partition Table
PCR6 Resume from S4 and S5 Power State Events
PCR7 Secure Boot State
PCR8 Hash of the booted kernel
PCR 9 to 10 Reserved for Future Use
PCR11 BitLocker Access Control
PCR12 Data events and highly volatile events
PCR13 Boot Module Details
PCR14 Boot Authorities
PCR 15 to 23 Reserved for Future Use

Для вклю­чения systemd-cryptsetup необ­ходимо добавить в /etc/mkinicpio.conf хуки systemd и sd-encrypt.

HOOKS=(base systemd modconf block keyboard sd-encrypt filesystems fsck)
$ mkinicpio -P

На­конец, изме­ним стро­ку заг­рузки, добавив парамет­ры rd.luks.name и rd.luks.name.

$ blkid /dev/sda2
/dev/sda2: UUID="e04b5b87-6bfc-4f73-83b0-36f91d52f141" TYPE="crypto_LUKS" PARTLABEL="primary" PARTUUID="136ffe26-5569-4c76-a871-9025ea52dbd8"

$ efibootmgr \
--create \
--bootnum=0001 \
--label "Arch Linux" \
--disk /dev/sda \
--part 1 \
--loader /vmlinuz-linux \
--unicode 'rd.luks.name=e04b5b87-6bfc-4f73-83b0-36f91d52f141=cryptroot rd.luks.options=tpm2-device=auto root=/dev/mapper/cryptroot rootflags=subvol=@root rw initrd=\initramfs-linux.img' \
--verbose

Пе­резаг­ружа­емся и про­веря­ем, что все работа­ет как надо.

Для тех, кому systemd-cryptsetup по каким‑то при­чинам не под­ходит (нап­ример, вер­сия systemd ста­рая или вооб­ще нет systemd), могу пореко­мен­довать фрей­мворк Clevis. С его помощью мож­но реали­зовать сле­дующие сце­нарии авто­раз­бло­киров­ки LUKS:

  • tpm2 — раз­бло­киров­ка с TPM;
  • tang — раз­бло­киров­ка с сетево­го сер­вера.

Вот как исполь­зовать Сlevis с TPM:

$ clevis luks bind -d /dev/sda2 tpm2 '{"pcr_bank":"sha1","pcr_ids":"0,4"}'
Enter existing LUKS password: ***************

Про­веря­ем.

$ sudo clevis luks list -d /dev/sda2
1: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"0,4"}

Ва­риант с сер­вером tang нас­тра­ивает­ся не слож­нее (разуме­ется, заранее раз­ворачи­ваем tang и обес­печива­ем сетевую дос­тупность):

$ sudo clevis luks bind -d /dev/sda2 tang '{"url":"http://192.168.88.101:7500"}'
The advertisement contains the following signing keys:

QjvwIdUeor3gBqxLteIOKvWPRqM

Do you wish to trust these keys? [ynYN] y
Enter existing LUKS password:

Про­веря­ем.

$ sudo clevis luks list -d /dev/sda2
1: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha1","pcr_ids":"0,4"}'
2: tang '{"url":"http://192.168.88.101:7500"}'

В зак­лючение темы авто­раз­бло­киров­ки сто­ит упо­мянуть некото­рые огра­ниче­ния фрей­мвор­ка Clevis:

  • Clevis работа­ет толь­ко с TPM 2.0; TPM 1.2 не под­держи­вает­ся.
  • В Tang не реали­зован механизм сме­ны клю­чей.
  • Для Tang необ­ходима дос­тупность сети на эта­пе заг­рузки, поэто­му, нап­ример, для Wi-Fi-под­клю­чения тре­бует­ся допол­нитель­ная нас­трой­ка адап­тера в initrd.
 

Создание ключей восстановления

Бэ­капы клю­чей в таком деле, как шиф­рование, дол­жны быть, об этом зна­ет каж­дый.

Есть два вари­анта соз­дания клю­чей вос­ста­нов­ления:

  • дамп заголов­ка и мас­тер‑клю­ча;
  • за­пись пароля вос­ста­нов­ления в Active Directory.
 

Делаем копию мастер-ключа

Сде­лать резер­вную копию заголов­ка и мас­тер‑клю­ча мож­но с помощью хорошо извес­тной нам ути­литы cryptsetup.

warning

С помощью соз­данной копии мож­но будет получить дос­туп к раз­делу, не зная пароль, поэто­му нас забот­ливо пре­дуп­режда­ют, что хра­нить ее нуж­но в сек­рете!

$ cryptsetup luksDump --dump-master-key /dev/sda2 --master-key-file mk.dump

WARNING!
========
The header dump with volume key is sensitive information
that allows access to encrypted partition without a passphrase.
This dump should be stored encrypted in a safe place.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sda2:
LUKS header information for /dev/sda2
Cipher name: aes
Cipher mode: xts-plain64
Payload offset: 32768
UUID: e04b5b87-6bfc-4f73-83b0-36f91d52f141
MK bits: 512
Key stored to file mk.dump

$ xxd mk.dump
00000000: 1501 eccc 84fd 82f7 f53b 56ae 4ab8 5775 .........;V.J.Wu
00000010: 4968 ea61 8d01 1f29 985e 33af 5537 ba40 Ih.a...).3.U7.@
00000020: af61 6aa2 b384 afe1 aff6 d4c9 ad95 1d67 .aj............g
00000030: 3a4f d64f 33ea 6f50 0a18 1139 9196 d694 :O.O3.oP...9....

При необ­ходимос­ти мож­но вос­ста­новить заголо­вок и уста­новить новый пароль.

$ cryptsetup luksAddKey /dev/sda2 --master-key-file mk.dump
Enter new passphrase for key slot: ********
Verify passphrase: ********

 

Добавляем пароль восстановления в Active Directory

Дру­гой инте­рес­ный спо­соб — это хра­нение пароля в AD.

Для начала нуж­но вклю­чить нашу Arch Linux в домен. Делать это буду с помощью System Security Services Daemon (SSSD) и realmd.

Ус­танав­лива­ем sssd обыч­ным спо­собом:

$ pacman -Sy sssd

А realmd ста­вим из AUR.

Убе­дим­ся, что домен дос­тупен.

$ realm discover ad.localdomain
ad.localdomain
type: kerberos
realm-name: AD.LOCALDOMAIN
domain-name: ad.localdomain
configured: no
server-software: active-directory
client-software: sssd

При­соеди­няем­ся к домену, для это­го понадо­бит­ся учет­ная запись с пра­вами вво­да в домен (для прос­тоты — administrator).

$ realm join ad.localdomain
Password for Administrator: ********

Про­веря­ем, что все прош­ло успешно и мы получи­ли билеты Kerberos:

$ realm list
ad.localdomain
type: kerberos
realm-name: AD.LOCALDOMAIN
domain-name: ad.localdomain
configured: kerberos-member
server-software: active-directory
client-software: sssd
login-formats: %U@ad.localdomain
login-policy: allow-realm-logins
$ klist -k
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
2 ARCH$@AD.LOCALDOMAIN
2 ARCH$@AD.LOCALDOMAIN
2 ARCH$@AD.LOCALDOMAIN
2 host/ARCH@AD.LOCALDOMAIN
2 host/ARCH@AD.LOCALDOMAIN
2 host/ARCH@AD.LOCALDOMAIN
2 RestrictedKrbHost/ARCH@AD.LOCALDOMAIN
2 RestrictedKrbHost/ARCH@AD.LOCALDOMAIN
2 RestrictedKrbHost/ARCH@AD.LOCALDOMAIN

Па­роль вос­ста­нов­ления будем хра­нить в учет­ной записи компь­юте­ра: это дочер­ний объ­ект клас­са msFVE-RecoveryInformation. Так дела­ет вин­довый BitLocker, ана­логич­но пос­тупим и мы.

За­писы­вать пароль в AD буду с помощью скрип­та на Python.

$ git clone https://github.com/ambalabanov/lukscrow
$ cd lukscrow/
$ pip install -r requirements.txt

За­пус­каем ути­литу, ука­зав необ­ходимые парамет­ры и текущий ключ (пароль):

$ ./lukscrow --luks-device /dev/sda2 --ldap-url "ldap://ad.localdomain" --computers-base-dn "CN=Computers,DC=ad,DC=localdomain" --unlock-key ********
Using Kerberos credential cache /tmp/tmpcsdyn49r
Connected to AD as 'u:AD\ARCH$'
Generating recovery key...
Adding recovery key to LUKS volume...
New key added.
Storing recovery key in Active Directory
Recovery key saved to AD.
Remove Kerberos credential cache /tmp/tmpcsdyn49r
Removed cache file.

Про­веря­ем в AD, что пароль вос­ста­нов­ления успешно записал­ся.

rp
rp

Ну и напос­ледок про­веря­ем сам пароль.

$ sudo cryptsetup --test-passphrase luksOpen /dev/sda2 && echo TRUE || echo FALSE
Enter passphrase for /dev/sda2: 287781-171655-523297-666928-307170-072073-039516-177220
TRUE

 

Заключение

Итак, мы уста­нови­ли Arch Linux на шиф­рован­ный кор­невой раз­дел, нас­тро­или авто­раз­бло­киров­ку при стар­те и позабо­тились о резер­вирова­нии клю­чей. Наде­юсь, для тебя это тоже было увле­катель­ным прик­лючени­ем!

Оцени статью:

Что тебе понравилось больше всего?
Что тебе не понравилось больше всего?